package com.gqyin.demo.service.impl;

import com.gqyin.demo.dao.BaseRepository;
import com.gqyin.demo.exception.ResourceNotFoundException;
import com.gqyin.demo.service.BaseService;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;

/**
 * @author guiqing.yin
 * 2024-04-11 11:49
 */
@Service
public abstract class BaseServiceImpl<T, ID> implements BaseService<T, ID> {

    public static final Logger LOGGER = LoggerFactory.getLogger(BaseServiceImpl.class);

    private final BaseRepository<T, ID> baseRepository;
    private final Class<T> entityClass;

    @SuppressWarnings("unchecked")
    @Autowired
    public BaseServiceImpl(BaseRepository<T, ID> baseRepository) {
        this.baseRepository = baseRepository;
        // 通过反射获取实体类的类型
        this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    @Override
    public List<T> findAll() {
        return baseRepository.findAll();
    }

    @Override
    public T findById(@NotNull ID id) {
        T existing = baseRepository.findById(id);
        if (existing == null) {
            String message = String.format("%s with id=%s NOT FOUND", entityClass.getSimpleName(), id);
            throw new ResourceNotFoundException(message);
        }
        return existing;
    }

    @Override
    public Page<T> findByPage(@NotNull PageRequest pageRequest) {
        return baseRepository.findByPage(pageRequest);
    }

    @Override
    public T save(@NotNull T t) {
        return baseRepository.save(t);
    }

    @Override
    public T updateById(@NotNull T t, @NotNull ID id) {
        T existing = baseRepository.findById(id);
        if (existing == null) {
            String message = String.format("%s with id=%s NOT FOUND", entityClass.getSimpleName(), id);
            throw new ResourceNotFoundException(message);
        }
        try {
            this.copyNonNullProperties(t, existing);
        } catch (IllegalAccessException e) {
            LOGGER.error("Copy properties exception. Source: {}. Target: {}.", t, existing);
            throw new RuntimeException(e);
        }
        return baseRepository.save(existing);
    }

    @Override
    public T deleteById(@NotNull ID id) {
        return baseRepository.deleteById(id);
    }

    private void copyNonNullProperties(T source, T target) throws IllegalAccessException {
        Validate.notNull(source, "Source must not be null");
        Validate.notNull(target, "Target must not be null");
        Field[] sourceFields = source.getClass().getDeclaredFields();
        Field[] targetFields = target.getClass().getDeclaredFields();
        for (Field sourceField : sourceFields) {
            sourceField.setAccessible(true);
            String fieldName = sourceField.getName();
            Object sourceValue = sourceField.get(source);
            if (sourceValue != null) {
                for (Field targetField : targetFields) {
                    targetField.setAccessible(true);
                    if (fieldName.equals(targetField.getName())) {
                        targetField.set(target, sourceValue);
                    }
                }
            }
        }
    }
}
